from pickletools import uint8
import cv2
import numpy as np
from astropy.utils.metadata.utils import dtype
from numba import uint8
from numpy.ma.core import resize
from openpyxl.styles.builtins import output
from skimage.io import imread

def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

def resize(image,width=None,height=None,inter=cv2.INTER_AREA):
    dim=None
    (h,w) = image.shape[:2]
    if width is None and height is None:
        return image
    if width is None :
        r = height / float(h)
        dim = (int(r*w),height)
    else:
        r = width / float(w)
        dim = (width,int(r*h))
    resized = cv2.resize(image,dim,interpolation=inter)
    return resized

# 参数1：需要排序的轮廓， 参数2：需要排序的方式，例如：left-to-right 从左到右
def sort_contours(cnts, method='left-to-right'):
    # reverse=True 从大到小
    reverse = False  # 从小到大
    # 表示用第一个参数 x坐标值进行排序
    i = 0
    if method == 'right-to-left' or method == 'bottom-to-top':
        reverse = True
    if method == 'top-to-bottom' or method == 'bottom-to-top':
        # 表示用第二个参数 y坐标进行排序
        i = 1
    # cv2.boundingRect(c) 获取轮廓左上角的坐标及高宽值
    bounding_boxes = [cv2.boundingRect(c) for c in cnts]  # 用一个最小的矩形, 把找到的形状包起来(x, y, h, w)
    # zip将数据打包成元组形式, 例如:a[1],b[1]为一组    zip(*) * 默认将文件解压成列表
    # key=lambda b: b[1][i] b[1]表示为对第二个参数即bounding_boxes进行排序 ；b[1][i] 表示对bounding_boxes中的第i个数据进行排序(0对x， 1对y)
    (cnts, bounding_boxes) = zip(*sorted(zip(cnts, bounding_boxes), key=lambda b: b[1][i], reverse=reverse))
    return cnts, bounding_boxes

def order_point(pts):
    #一共四个坐标点
    rect = np.zeros((4,2),dtype="float32")
    #按顺序找到坐标点0123分别是左上、右上、右下和左下
    #计算左上和右下
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]
    #计算右上和左下
    diff = np.diff(pts,axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

def four_point_transform(image,pts):
    rect = order_point(pts)
    (tl,tr,br,bl) = rect
    #计算width和height
    width1 = np.sqrt( ((br[0]-bl[0])**2) + ((br[1]-bl[1])**2) )
    width2 = np.sqrt( ((tr[0]-tl[0])**2) + ((tr[1]-tl[1])**2) )
    maxwidth = max(int(width1),int(width2))

    height1 = np.sqrt( ((tr[0]-br[0])**2) + ((tr[1]-br[1])**2) )
    height2 = np.sqrt( ((tl[0]-bl[0])**2) + ((tl[1]-bl[1])**2) )
    maxheight = max(int(height1),int(height2))

    #变换后对应坐标点
    dst = np.array([
        [0,0],
        [maxwidth-1,0],
        [maxwidth-1,maxheight-1],
        [0,maxheight-1]],dtype = "float32"
    )

    #计算变换矩阵
    M = cv2.getPerspectiveTransform(rect,dst)
    warped = cv2.warpPerspective(image,M,(maxwidth,maxheight))
    return warped

#读取数字模板
img = imread('C:/Users/nic/Desktop/opencv/picture/documents/ticket.png')
#img = imread('C:/Users/nic/Desktop/opencv/picture/documents/document1.jpg')
image = img.copy()
orig = image.copy()
#shape获得的是图像的 h 和 w 而cv2.resize函数则是先 w 再 h
ratio = image.shape[0]/600
#等比例放缩
image = resize(image,height=600)
#转化为灰度图
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
#高斯滤波
gray = cv2.GaussianBlur(gray,(9,9),0)
#canny边缘检测
edged = cv2.Canny(gray,100,200)
#cv_show('s',edged)
#轮廓检测
contours, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#只要找出最大的轮廓
cnts = sorted(contours,key=cv2.contourArea,reverse = True)
#cv2.drawContours(image,contours,-1,(0,0,255),2)
for c in cnts:
    peri = cv2.arcLength(c,True)
    approx = cv2.approxPolyDP(c,0.02*peri,True)
    if len(approx)==4:
        screenCnt = approx
        break

cv2.drawContours(image,[screenCnt],-1,(0,0,255),2)
cv_show('s',image)
#透视变换
orig = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
cv_show('s',orig)





